import { IRawContact } from "@/chatlog_types/IRawContact";
import { is_raw_contact_item_changed } from "@/chatlog_types/is_raw_contact_item_changed";
import { ContactType } from "@/constants/ContactType";
import { GlobalValue } from "@/GlobalValue";
import Callbacks from "@/utils/Callbacks";
import { Poller } from "@/utils/Poller";
import axios, { isAxiosError } from "axios";
import localforage from "localforage";
import { ContactListWraper } from "./ContactListWraper";
import { create_contact_commit_job } from "./create_contact_commit_job";
import { IContactItem } from "./IContactItem";
import { tasks_keeper } from "../tasks/instance";

export interface ContactsMgrCallbacks {
  on_loading_changed?(mgr: ContactsMgr, is_loading: boolean): void;
  on_list_change?(mgr: ContactsMgr, value: IContactItem[], prev: IContactItem[]): void;
  on_item_change?(mgr: ContactsMgr, value: IContactItem, prev?: IContactItem): void;
}
export class ContactsMgr {
  static readonly TAG = 'ContactsMgr';
  readonly callbacks = new Callbacks<ContactsMgrCallbacks>()
  readonly list_wrapper = new ContactListWraper(this)
    .ignore_order()
    .on_item_change((a, b) => this.callbacks.emit('on_item_change')(this, a, b))
    .on_list_change((a, b) => this.callbacks.emit('on_list_change')(this, a, b))
  readonly poller = new Poller(() => this.load()).set_interval(30000)
  get map(): ReadonlyMap<string, IContactItem> { return this.list_wrapper.map }
  get list(): IContactItem[] { return this.list_wrapper.list }
  get error() { return this.poller.error; };
  get loading() { return this.poller.loading; }
  keyword?: string;
  start() {
    this.poller.start();
  }
  stop() {
    this.poller.stop();
  }
  async load() {
    const http_addr = GlobalValue.current_state.chatlog_state?.http_addr
    if (!http_addr) return;
    const { jid } = this.poller;
    const { keyword } = this;
    const url = `http://${http_addr}/api/v1/contact`
    const params = { format: 'json', keyword }
    this.callbacks.emit('on_loading_changed')(this, true);
    return axios(url, { responseType: 'json', params })
      .then((reply: any) => {
        if (jid != this.poller.jid) return [];
        if (!reply || !Array.isArray(reply?.data))
          console.warn(`[${ContactsMgr.TAG}.load] 'reply.data' must be array!`)

        const raw_list: IRawContact[] = reply?.data ?? []
        this.list_wrapper.set_raw_list(raw_list)
        return this.list_wrapper.list
      }).catch((e) => {
        return Promise.reject(
          isAxiosError(e) && typeof e.response?.data === 'string' ?
            new Error(`[${e.response.status}]${e.response?.data}`) : e
        );
      }).finally(() => {
        if (jid != this.poller.jid) return;
        this.callbacks.emit('on_loading_changed')(this, false);
      })
  }

  submit(item: IContactItem) {
    const { raw } = item;
    const id = tasks_keeper().unshift(create_contact_commit_job(raw))
    if (!tasks_keeper().auto_submit) tasks_keeper().start(id)
  }
}
export const contacts_mgr = (window as any).contacts_mgr = new ContactsMgr();
export const contacts_forage = localforage.createInstance({ name: 'contacts_forage' })

async function need_update(raw: IRawContact): Promise<boolean> {
  const key = `contact_item##${raw.account}##${raw.userName}`;
  return contacts_forage.getItem<IRawContact>(key).then((prev) => {
    if (!prev) return true;
    return is_raw_contact_item_changed(raw, prev);
  }).catch(e => {
    console.error('IRawContact: need_update() failed, e', e)
    return false
  })
}

contacts_mgr.callbacks.add({
  on_item_change(_, { raw }) {
    if (!raw.isFriend) return;
    switch (ContactType.guess_contact_type(raw.userName)) {
      case ContactType.Value.服务号:
      case ContactType.Value.工具:
      case ContactType.Value.OPENIM:
        return;
    }
    need_update(raw).then((n) => n && tasks_keeper().push(create_contact_commit_job(raw)))
  }
})
